package cn.vivi.seckill.interceptor;

import cn.vivi.seckill.anno.AccessLimit;
import cn.vivi.seckill.pojo.User;
import cn.vivi.seckill.vo.RespBean;
import cn.vivi.seckill.vo.RespBeanEnum;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Objects;
import java.util.concurrent.TimeUnit;


@Component
public class AccessLimitInterceptor implements HandlerInterceptor {

    @Autowired
    RedisTemplate redisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod hm = (HandlerMethod) handler;
            AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);
            if (Objects.isNull(accessLimit)) {
                return true;
            }
            int second = accessLimit.second();
            int maxCount = accessLimit.maxCount();
            boolean needLogin = accessLimit.needLogin();
            String uri = request.getRequestURI();
            if (needLogin) {
                User user = getUser(request);
                if (Objects.isNull(user)) {
                    toRender(response, RespBeanEnum.USER_NO_LOGIN);
                    return false;
                }
                uri = uri + ":" + user.getPhone();
            }
            return toLimit(response, second, maxCount, uri);
        }
        return true;
    }

    // 简单计数器限流
    private boolean toLimit(HttpServletResponse response, int second, int maxCount, String uri) throws IOException {
        ValueOperations valueOperations = redisTemplate.opsForValue();
        Integer count = (Integer) valueOperations.get(uri);
        if (Objects.isNull(count)) {
            valueOperations.set(uri, 1, second, TimeUnit.SECONDS);
        } else if (count < maxCount) {
            // 计数器加一
            valueOperations.increment(uri);
        } else {
            // 超出访问限制
            toRender(response, RespBeanEnum.ACCESS_LIMIT);
            return false;
        }
        return true;
    }

    private void toRender(HttpServletResponse response, RespBeanEnum respBeanEnum) throws IOException {
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        PrintWriter writer = response.getWriter();
        writer.write(new ObjectMapper().writeValueAsString(RespBean.error(respBeanEnum)));
        writer.flush();
        writer.close();
    }

    private User getUser(HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();
        String userTicket = "";
        for (Cookie cookie : cookies) {
            if ("userTicket".equals(cookie.getName())) {
                userTicket = cookie.getValue();
            }
        }
        return (User) redisTemplate.opsForValue().get("user:" + userTicket);
    }
}
